package x86;

import assem.Instr;
import tree.Stm;
import tree.Exp;
import tree.MOVE;
import tree.EXPSTM;
import tree.CJUMP;
import tree.CALL;
import tree.LABEL;
import tree.JUMP;
import tree.MEM;
import tree.BINOP;
import tree.CONST;
import tree.ESEQ;
import tree.TEMP;
import tree.NAME;
import temp.Temp;
import temp.Label;
import util.List;
import util.Pair;

public class Codegen
{
    Frame frame;
    public Codegen(Frame f)
    {
        frame=f;        
    }

    private List<Instr> ilist=null;
    private List<Instr> last=null;

    private void emit(Instr inst)
    {
        if (last!=null)
            last = last.tail = new List<Instr>(inst,null);
        else 
            last = ilist = new List<Instr>(inst,null);
    }

    private void munchStm (Stm s) {
	//emit(new assem.OPER("nop", null, null));
	if(s instanceof MOVE)
	    munchMove((MOVE)s);
	else if(s instanceof EXPSTM)
	    munchExpStm((EXPSTM)s);
	else if(s instanceof CJUMP)
	    munchCjump((CJUMP)s);
	else if(s instanceof LABEL)
	    munchLabel((LABEL)s);
	else if(s instanceof JUMP)
	    munchJump((JUMP)s);
	else
	    throw new Error("Unhandled: " + s.getClass());
    }

    private void munchMove(MOVE s){
	ExpResult resSrc = munchExp(s.src);
	ExpResult resDst = munchExp(s.dst);
	if(resDst instanceof MemExp){
	    MemExp dme = (MemExp)resDst;
	    if(resSrc instanceof ConstExp){
		emit(new assem.OPER("mov dword " + dme.address + ", " + ((ConstExp)resSrc).value,
				    null,
				    dme.src));
	    }else if(resSrc instanceof TempExp){
		List<Temp> src = dme.src.clone();
		List<Temp> sl;
		for(sl = src; sl.tail!=null; sl = sl.tail);
		sl.tail = new List<Temp>(((TempExp)resSrc).temp, null);
		emit(new assem.OPER("mov " + dme.address + ", `s1",
				    null,
				    src));
	    }else if(resSrc instanceof MemExp){
		// MEM <- MEM nao pode, joga em temporario
		List<Temp> src = dme.src.clone();
		List<Temp> sl;
		for(sl = src; sl.tail!=null; sl = sl.tail);
		sl.tail = new List<Temp>(((MemExp)resSrc).toTemp(), null);
		emit(new assem.OPER("mov " + dme.address + ", `s1",
				    null,
				    src));
	    }
	}else if(resDst instanceof TempExp){
	    TempExp dte = (TempExp)resDst;
	    if(resSrc instanceof ConstExp){
		emit(new assem.OPER("mov `d0, " + ((ConstExp)resSrc).value,
				    new List<Temp>(dte.temp, null),
				    null));
	    }else if(resSrc instanceof TempExp){
		emit(new assem.MOVE("mov `d0, `s0",
				    dte.temp,
				    ((TempExp)resSrc).temp));
	    }else if(resSrc instanceof MemExp){
		MemExp sme = (MemExp)resSrc;
		emit(new assem.OPER("mov `d0, " + sme.address,
				    new List<Temp>(dte.temp, null),
				    sme.src));
	    }
	}
    }

    private void munchExpStm(EXPSTM s){
	munchExp(s.exp);
    }

    private void munchCjump(CJUMP s){
	ExpResult resLeft = munchExp(s.left);
	ExpResult resRight = munchExp(s.right);
	if(resLeft instanceof ConstExp){
	    if(resRight instanceof ConstExp){
		// duas constantes! resolvendo em tempo de compilacao :)
		long c1 = ((ConstExp)resLeft).value;
		long c2 = ((ConstExp)resRight).value;
		switch(s.op){
		case CJUMP.EQ:
		    if(c1 == c2)
			emit(new assem.OPER("jmp `j0", null, null, new List<Label>(s.ifTrue, null)));
		    break;
		case CJUMP.NE:
		    if(c1 != c2)
			emit(new assem.OPER("jmp `j0", null, null, new List<Label>(s.ifTrue, null)));
		    break;
		case CJUMP.LT:
		    if(c1 < c2)
			emit(new assem.OPER("jmp `j0", null, null, new List<Label>(s.ifTrue, null)));
		    break;
		case CJUMP.LE:
		    if(c1 <= c2)
			emit(new assem.OPER("jmp `j0", null, null, new List<Label>(s.ifTrue, null)));
		    break;
		case CJUMP.GT:
		    if(c1 > c2)
			emit(new assem.OPER("jmp `j0", null, null, new List<Label>(s.ifTrue, null)));
		    break;
		case CJUMP.GE:
		    if(c1 >= c2)
			emit(new assem.OPER("jmp `j0", null, null, new List<Label>(s.ifTrue, null)));
		    break;
		default:
		    throw new Error("Invalid cjump operator, code: " + s.op);
		}
	    }else if(resRight instanceof TempExp){
		// invertendo a ordem pq imediato deve estar no segundo op
		s.op = CJUMP.notRel(s.op);
		emit(new assem.OPER("cmp `s0, " + ((ConstExp)resLeft).value,
				    null,
				    new List<Temp>(((TempExp)resRight).temp, null)));
	    }else if(resRight instanceof MemExp){
		// invertendo a ordem pq imediato deve estar no segundo op
		s.op = CJUMP.notRel(s.op);
		ConstExp lce = (ConstExp)resLeft;
		MemExp rce = (MemExp)resRight;
		emit(new assem.OPER("cmp dword " + rce.address + ", " + lce.value,
				    null,
				    rce.src));
	    }
	}else if(resLeft instanceof TempExp){
	    if(resRight instanceof ConstExp){
		emit(new assem.OPER("cmp `s0, " + ((ConstExp)resRight).value,
				    null,
				    new List<Temp>(((TempExp)resLeft).temp, null)));		
	    }else if(resRight instanceof TempExp){
		emit(new assem.OPER("cmp `s0, `s1",
				    null,
				    new List<Temp>(((TempExp)resLeft).temp, new List<Temp>(((TempExp)resRight).temp, null))));
	    }else if(resRight instanceof MemExp){
		MemExp rce = (MemExp)resRight;
		List<Temp> src = rce.src;
		List<Temp> sl;
		for(sl = src; sl.tail!=null; sl=sl.tail);
		sl.tail = new List<Temp>(((TempExp)resLeft).temp, null);
		emit(new assem.OPER("cmp `s1, " + rce.address,
				    null,
				    src));
	    }
	}else if(resLeft instanceof MemExp){
	    MemExp lme = (MemExp)resLeft;
	    if(resRight instanceof ConstExp){
		emit(new assem.OPER("cmp dword " + lme.address + ", " + ((ConstExp)resRight).value,
				    null,
				    lme.src));
	    }else if(resRight instanceof TempExp){
		List<Temp> src = lme.src.clone();
		List<Temp> sl;
		for(sl = src; sl.tail!=null; sl=sl.tail);
		sl.tail = new List<Temp>(((TempExp)resRight).temp, null);
		emit(new assem.OPER("cmp " + lme.address + ", `s1",
				    null,
				    src));
	    }else if(resRight instanceof MemExp){
		// dois acessos a memoria nao pode...
		MemExp rme = (MemExp)resRight;
		List<Temp> src = rme.src.clone();
		List<Temp> sl;
		for(sl = src; sl.tail!=null; sl=sl.tail);
		sl.tail = new List<Temp>(lme.toTemp(), null);
		emit(new assem.OPER("cmp `s1, " + rme.address,
				    null,
				    src));
	    }
	}

	switch(s.op){
	case CJUMP.EQ:
	    emit(new assem.OPER("je `j0", null, null, new List<Label>(s.ifTrue, new List<Label>(s.ifFalse, null))));
	    break;
	case CJUMP.NE:
	    emit(new assem.OPER("jne `j0", null, null, new List<Label>(s.ifTrue, new List<Label>(s.ifFalse, null))));
	    break;
	case CJUMP.LT:
	    emit(new assem.OPER("jl `j0", null, null, new List<Label>(s.ifTrue, new List<Label>(s.ifFalse, null))));
	    break;
	case CJUMP.LE:
	    emit(new assem.OPER("jle `j0", null, null, new List<Label>(s.ifTrue, new List<Label>(s.ifFalse, null))));
	    break;
	case CJUMP.GT:
	    emit(new assem.OPER("jg `j0", null, null, new List<Label>(s.ifTrue, new List<Label>(s.ifFalse, null))));
	    break;
	case CJUMP.GE:
	    emit(new assem.OPER("jge `j0", null, null, new List<Label>(s.ifTrue, new List<Label>(s.ifFalse, null))));
	    break;
	case CJUMP.ULT:
	    emit(new assem.OPER("jb `j0", null, null, new List<Label>(s.ifTrue, new List<Label>(s.ifFalse, null))));
	    break;
	case CJUMP.ULE:
	    emit(new assem.OPER("jbe `j0", null, null, new List<Label>(s.ifTrue, new List<Label>(s.ifFalse, null))));
	    break;
	case CJUMP.UGT:
	    emit(new assem.OPER("ja `j0", null, null, new List<Label>(s.ifTrue, new List<Label>(s.ifFalse, null))));
	    break;
	case CJUMP.UGE:
	    emit(new assem.OPER("jae `j0", null, null, new List<Label>(s.ifTrue, new List<Label>(s.ifFalse, null))));
	    break;
	default:
	    throw new Error("Invalid cjump operator, code: " + s.op);
	}
    }

    private void munchLabel(LABEL s){
	emit(new assem.LABEL(s.label + ":\n", s.label));
    }

    private void munchJump(JUMP s){
	if(s.exp instanceof NAME){
	    // jmp label
	    NAME l = (NAME)s.exp;
	    emit(new assem.OPER("jmp `j0", null, null, new List<Label>(l.label, null)));
	}else{
	    // jmp _temp
	    ExpResult res = munchExp(s.exp);
	    Temp t = res.toTemp();
	    emit(new assem.OPER("jmp `s0", null, new List<Temp>(t, null), s.targets));	    
	}
    }

    /************************* MunchExp *************************/
    private ExpResult munchExp(Exp e){
	if(e instanceof MEM)
	    return munchExp((MEM) e);
	else if(e instanceof BINOP)
	    return munchExp((BINOP) e);
	else if(e instanceof CONST)
	    return munchExp((CONST) e);
	else if(e instanceof CALL)
	    return munchExp((CALL) e);
	else if(e instanceof ESEQ)
	    return munchExp((ESEQ) e);
	else if(e instanceof TEMP)
	    return munchExp((TEMP) e);
	else if(e instanceof NAME)
	    return munchExp((NAME) e);
	else
	    throw new Error("Unhandled exp: " + e.getClass());
    }

    private ExpResult munchExp(MEM e){
	Pair< String, List<Temp> > sl = adressingString(e, 0);
	if(sl == null){
	    ExpResult res = munchExp(e.exp);
	    Temp d = res.toTemp();
	    return new MemExp("[`s0]", new List<Temp>(d, null));
	}else{
	    return new MemExp(sl.first, sl.second);
	}
    }

    private ExpResult munchExp(BINOP e){
	ExpResult resLeft = munchExp(e.left);
	ExpResult resRight = munchExp(e.right);
	boolean rconst;

	switch(e.binop){
	case BINOP.PLUS:
	    if(resLeft instanceof ConstExp){
		// esquerda eh constante
		if(resRight instanceof ConstExp){
		    // duas constantes, resolvemos em tempo de compilacao :)
		    long c1 = ((ConstExp)resLeft).value;
		    long c2 = ((ConstExp)resRight).value;
		    return new ConstExp(c1+c2);
		}else{
		    // nao deu pra fazer em tempo de compilacao :(
		    long c1 = ((ConstExp)resLeft).value;
		    if(c1 == 0){
			// temp + 0 -> soma inutil!
			return resRight;
		    }else{
			// carrega segundo operando no temporario
			Temp t = new Temp();
			emit(new assem.MOVE("mov `d0, `s0", t, resRight.toTemp()));
			if(c1 == 1){
			    // soh incrementa
			    emit(new assem.OPER("inc `d0",
						new List<Temp>(t, null),
						new List<Temp>(t, null)));
			}else{
			    // somando...
			    emit(new assem.OPER("add `d0, " + c1,
						new List<Temp>(t, null),
						new List<Temp>(t, null)));
			}
			return new TempExp(t);
		    }
		}
	    }else{
		// esquerda nao eh constante
		if(resRight instanceof ConstExp){
		    long c2 = ((ConstExp)resRight).value;
		    if(c2 == 0){
			// temp + 0 -> soma inutil!
			return resLeft;
		    }else{
			// carrega primeiro operando no temporario
			Temp t = new Temp();
			emit(new assem.MOVE("mov `d0, `s0", t, resLeft.toTemp()));
			if(c2 == 1){
			    // soh incrementa
			    emit(new assem.OPER("inc `d0",
						new List<Temp>(t, null),
						new List<Temp>(t, null)));
			}else{
			    // somando...
			    emit(new assem.OPER("add `d0, " + c2,
						new List<Temp>(t, null),
						new List<Temp>(t, null)));
			}
			return new TempExp(t);
		    }
		}else{
		    // direita tambem nao eh constante
		    Temp t = new Temp();
		    emit(new assem.MOVE("mov `d0, `s0", t, resLeft.toTemp()));
		    // TODO MemExp
		    emit(new assem.OPER("add `d0, `s1",
					new List<Temp>(t, null),
					new List<Temp>(t, new List<Temp>(resRight.toTemp(), null))));
		    return new TempExp(t);
		}
	    }
	    //break;
	case BINOP.MINUS:
	    rconst = resRight instanceof ConstExp;
	    if(resLeft instanceof ConstExp && rconst){
		// duas constantes, resolvemos em tempo de compilacao :)
		long c1 = ((ConstExp)resLeft).value;
		long c2 = ((ConstExp)resRight).value;
		return new ConstExp(c1-c2);
	    }else{
		// nao deu pra fazer em tempo de compilacao :(
		if(rconst){
		    long c2 = ((ConstExp)resRight).value;
		    if(c2 == 0){
			// temp - 0 -> subtracao inutil!
			return resLeft;
		    }else{
			// carrega primeiro operando no temporario
			Temp t = new Temp();
			emit(new assem.MOVE("mov `d0, `s0", t, resLeft.toTemp()));
			if(c2 == 1){
			    // soh decrementa
			    emit(new assem.OPER("dec `d0",
						new List<Temp>(t, null),
						new List<Temp>(t, null)));
			}else{
			    // subtraindo...
			    emit(new assem.OPER("sub `d0, " + c2,
						new List<Temp>(t, null),
						new List<Temp>(t, null)));
			}
			return new TempExp(t);
		    }
		}else{
		    // direita nao eh constante
		    Temp t = new Temp();
		    emit(new assem.MOVE("mov `d0, `s0", t, resLeft.toTemp()));
		    emit(new assem.OPER("sub `d0, `s1",
					new List<Temp>(t, null),
					new List<Temp>(t, new List<Temp>(resRight.toTemp(), null))));
		    return new TempExp(t);
		}
	    }
	    //break;
	case BINOP.TIMES:
	    if(resLeft instanceof ConstExp){
		// esquerda eh constante
		if(resRight instanceof ConstExp){
		    // duas constantes, resolvemos em tempo de compilacao :)
		    long c1 = ((ConstExp)resLeft).value;
		    long c2 = ((ConstExp)resRight).value;
		    return new ConstExp(c1*c2);
		}else{
		    // nao deu pra fazer em tempo de compilacao :(
		    long c1 = ((ConstExp)resLeft).value;
		    if(c1 == 0){
			// temp * 0 -> zero!
			return new ConstExp(0);
		    }else if(c1 == 1){
			// temp * 1 -> temp!
			return resRight;
		    }else{
			// carrega segundo operando no temporario
			Temp t = new Temp();
			emit(new assem.MOVE("mov `d0, `s0", t, resRight.toTemp()));
			// multiplicando...
			emit(new assem.OPER("imul `d0, " + c1,
					    new List<Temp>(t, null),
					    new List<Temp>(t, null)));
			return new TempExp(t);			
		    }
		}
	    }else{
		// esquerda nao eh constante
		if(resRight instanceof ConstExp){
		    long c2 = ((ConstExp)resRight).value;
		    if(c2 == 0){
			// temp * 0 -> zero!
			return new ConstExp(0);
		    }else if(c2 == 1){
			// temp * 1 -> temp!
			return resLeft;
		    }else{
			// carrega primeiro operando no temporario
			Temp t = new Temp();
			emit(new assem.MOVE("mov `d0, `s0", t, resLeft.toTemp()));

			// multiplicando...
			emit(new assem.OPER("imul `d0, " + c2,
					    new List<Temp>(t, null),
					    new List<Temp>(t, null)));
			return new TempExp(t);
		    }
		}else{
		    // direita tambem nao eh constante
		    Temp t = new Temp();
		    emit(new assem.MOVE("mov `d0, `s0", t, resLeft.toTemp()));
		    emit(new assem.OPER("imul `d0, `s1",
					new List<Temp>(t, null),
					new List<Temp>(t, new List<Temp>(resRight.toTemp(), null))));
		    return new TempExp(t);
		}
	    }
	    //break;
	case BINOP.LSHIFT:
	    rconst = resRight instanceof ConstExp;
	    if(resLeft instanceof ConstExp && rconst){
		// duas constantes, resolvemos em tempo de compilacao :)
		long c1 = ((ConstExp)resLeft).value;
		long c2 = ((ConstExp)resRight).value;
		return new ConstExp(c1<<c2);
	    }else{
		Temp t = new Temp();
		emit(new assem.MOVE("mov `d0, `s0", t, resLeft.toTemp()));
		if(!rconst)
		    throw new Error("Right operand for shifting instruction must be constant");
		emit(new assem.OPER("shl `d0, " + ((ConstExp)resRight).value,
				    new List<Temp>(t, null),
				    new List<Temp>(t, null)));
		return new TempExp(t);
	    }
	    //break;
	default:
	    throw new Error("Invalid binary operator, code: " + e.binop);
	}
    }

    private ExpResult munchExp(CONST e){
	return new ConstExp(e.value);
    }

    private ExpResult munchExp(CALL e){
	// salva caller-save registers
	//pushCallerSaveRegisters();

	// push de parametros para pilha
	int pcount = pushParameters(e.args);

	// call da funcao
	if(e.func instanceof NAME){
	    emit(new assem.OPER("call " + ((NAME)e.func).label,
				new List<Temp>(Frame.esp, Frame.calldefs),
				new List<Temp>(Frame.esp, null)));
	}else{
	    ExpResult r = munchExp(e.func);
	    if(r instanceof ConstExp){
		emit(new assem.OPER("call " + ((ConstExp)r).value,
				    new List<Temp>(Frame.esp, Frame.calldefs),
				    new List<Temp>(Frame.esp, null)));
	    }else if(r instanceof TempExp){
		emit(new assem.OPER("call `s0",
				    new List<Temp>(Frame.esp, Frame.calldefs),
				    new List<Temp>(((TempExp)r).temp, new List<Temp>(Frame.esp, null))));
	    }else if(r instanceof MemExp){
		List<Temp> src = ((MemExp)r).src;
		List<Temp> s;
		for(s=src; s.tail!=null; s=s.tail);
		s.tail = new List<Temp>(Frame.esp, null);
		emit(new assem.OPER("call " + ((MemExp)r).address,
				    new List<Temp>(Frame.esp, Frame.calldefs),
				    src));
	    }
	}

	// restaura posicao da pilha
	if(pcount != 0)
	    emit(new assem.OPER("add esp, " + pcount,
				null,
				null));

	// restaura caller-save registers
	//popCallerSaveRegisters();

	// retorna temporario onde estah o registrador de retorno
	return new TempExp(frame.RV());
    }

    private ExpResult munchExp(ESEQ e){
	System.out.println("Naaaaao, nao era pra passar aqui (ESEQ)!!");
	System.exit(1);
	return null;
    }

    private ExpResult munchExp(TEMP e){
	return new TempExp(e.temp);
    }

    private ExpResult munchExp(NAME e){
	Temp t = new Temp();
	emit(new assem.OPER("mov `d0, " + e.label,
			    new List<Temp>(t, null),
			    null));
	return new TempExp(t);
    }
    /************************************************************/

    /************************* Auxiliares *************************/
    // emite push de parametros para chamada de metodo
    private int pushParameters(List<Exp> args){
	if(args == null){
	    return 0;
	}else{
	    int count = pushParameters(args.tail);
	    ExpResult res = munchExp(args.head);
	    if(res instanceof ConstExp){
		emit(new assem.OPER("push " + ((ConstExp)res).value,
				    new List<Temp>(Frame.esp, null),
				    new List<Temp>(Frame.esp, null)));
	    }else if(res instanceof TempExp){
		TempExp te = (TempExp)res;
		emit(new assem.OPER("push `s0",
				    new List<Temp>(Frame.esp, null),
				    new List<Temp>(te.temp, new List<Temp>(Frame.esp, null))));
	    }else if(res instanceof MemExp){
		MemExp me = (MemExp)res;
		List<Temp> src = me.src.clone();
		List<Temp> s;
		for(s = src; s.tail!=null; s=s.tail);
		s.tail = new List<Temp>(Frame.esp, null);
		emit(new assem.OPER("push dword " + me.address,
				    new List<Temp>(Frame.esp, null),
				    src));
	    }
	    return count + 4;
	}
    }

    // cria (se possivel) string para acessar memoria com algum modo
    // de enderecamento mais interessante que [_temp] :)
    private Pair< String, List<Temp> > adressingString(MEM e, int scount){
	Exp exp = e.exp;
	Pair< String, List<Temp> > ret = new Pair< String, List<Temp> >();
	if(exp instanceof BINOP){
	    BINOP binop = (BINOP)exp;
	    if(binop.left instanceof TEMP){
		if(binop.right instanceof CONST){
		    TEMP t = (TEMP) binop.left;
		    CONST c = (CONST) binop.right;
		    if(binop.binop == BINOP.PLUS){
			ret.first = "[ds:`s" + scount;
			if(c.value > 0)
			    ret.first += "+" + c.value + "]";
			else if(c.value < 0)
			    ret.first += c.value + "]";
			else
			    ret.first += "]";
			ret.second = new List<Temp>(t.temp, null);
			return ret;
		    }else if(binop.binop == BINOP.MINUS){
			ret.first = "[ds:`s" + scount;
			if(c.value > 0)
			    ret.first += "-" + c.value + "]";
			else if(c.value < 0)
			    ret.first += "+" + (-c.value) + "]";
			else
			    ret.first += "]";
			ret.second = new List<Temp>(t.temp, null);
			return ret;
		    }
		}else if(binop.right instanceof BINOP){
		    BINOP binop2 = (BINOP)binop.right;
		    // constantes??
		    if(binop2.left instanceof CONST && binop2.right instanceof CONST){
			// sim!! resolvendo em tempo de compilacao :)
			System.out.println("passei");
			long cleft = ((CONST)binop2.left).value;
			long cright = ((CONST)binop2.right).value;
			long c;
			switch(binop2.binop){
			case BINOP.PLUS:
			    c = cleft+cright;
			    break;
			case BINOP.MINUS:
			    c = cleft-cright;
			    break;
			case BINOP.TIMES:
			    c = cleft*cright;
			    break;
			case BINOP.LSHIFT:
			    c = cleft<<cright;
			    break;
			default:
			    return null;
			}

			// forma a string
			TEMP t = (TEMP) binop.left;
			if(binop.binop == BINOP.PLUS){
			    ret.first = "[ds:`s" + scount;
			    if(c > 0)
				ret.first += "+" + c + "]";
			    else if(c < 0)
				ret.first += c + "]";
			    else
				ret.first += "]";
			    ret.second = new List<Temp>(t.temp, null);
			    return ret;
			}else if(binop.binop == BINOP.MINUS){
			    ret.first = "[ds:`s" + scount;
			    if(c > 0)
				ret.first += "-" + c + "]";
			    else if(c < 0)
				ret.first += "+" + (-c) + "]";
			    else
				ret.first += "]";
			    ret.second = new List<Temp>(t.temp, null);
			    return ret;
			}
		    }
		}
	    }
	}else if(exp instanceof CONST){
	    CONST c = (CONST)exp;
	    ret.first = "[" + c.value + "]";
	    ret.second = null;
	    return ret;
	}

	return null;
    }

    // testa igualdade de duas Exp's
    // os casos que nao interessam sempre retornam false :)
    private boolean cmpExp(Exp e1, Exp e2){
	if(e1 instanceof MEM && e2 instanceof MEM){
	    return cmpExp(((MEM)e1).exp, ((MEM)e2).exp);
	}else if(e1 instanceof BINOP){
	    BINOP b1 = (BINOP)e1;
	    BINOP b2 = (BINOP)e2;
	    return (b1.binop==b2.binop && cmpExp(b1.left, b2.left) && cmpExp(b1.right, b2.right));
	}else if(e1 instanceof CONST && e2 instanceof CONST){
	    return (((CONST)e1).value == ((CONST)e2).value);
	}else if(e1 instanceof TEMP && e2 instanceof TEMP){
	    return (((TEMP)e1).temp.toString().equals(((TEMP)e2).temp.toString()));
	}else{
	    return false;
	}
    }

    // push caller-save registers -- NAO PRECISOU
    private void pushCallerSaveRegisters(){
	for(List<Temp> reg = Frame.callerSave; reg!=null; reg = reg.tail){
	    String regName = frame.tempMap(reg.head);
	    emit(new assem.OPER("push " + regName,
				null,
				null));
	}
    }

    // pop caller-save registers -- NAO PRECISOU
    private void popCallerSaveRegisters(){
	popCallerSaveRegisters(Frame.callerSave);
    }
    private void popCallerSaveRegisters(List<Temp> reg){
	if(reg == null)
	    return;
	popCallerSaveRegisters(reg.tail);
	String regName = frame.tempMap(reg.head);
	emit(new assem.OPER("pop " + regName,
			    null,
			    null));
    }
    /*************************************************************/
        
    /*-------------------------------------------------------------*
     *                              MAIN                           *
     *-------------------------------------------------------------*/
    List<Instr> codegen(Stm s)
    {
        List<Instr> l;
        munchStm(s);
        l=ilist;
        ilist=last=null;
        return l;
    }
    
    List<Instr> codegen(List<Stm> body)
    {
        List<Instr> l = null, t = null;
        
        for( ; body != null; body = body.tail )
	    {
		munchStm(body.head);
		if ( l == null )
		    l = ilist;
		else
		    t.tail = ilist;
		t = last;
		ilist=last=null;
	    }
        return l;
    }

    /****************************************************/
    private abstract class ExpResult
    {
	public ExpResult(){
	    super();
	}

	abstract public Temp toTemp();
    }

    private class TempExp extends ExpResult
    {
	Temp temp;

	public TempExp(Temp t){
	    temp = t;
	}

	public Temp toTemp(){
	    return temp;
	}
    }

    private class MemExp extends ExpResult
    {
	String address;
	List<Temp> src;

	public MemExp(String s, List<Temp> l){
	    address = s;
	    src = l;
	}

	public Temp toTemp(){
	    Temp t = new Temp();
	    emit(new assem.OPER("mov `d0, " + address,
				new List<Temp>(t, null),
				src));
	    return t;
	}
    }

    private class ConstExp extends ExpResult
    {
	long value;

	public ConstExp(long v){
	    value = v;
	}

	public Temp toTemp(){
	    Temp t = new Temp();
	    emit(new assem.OPER("mov `d0, " + value,
				new List<Temp>(t, null),
				null));
	    return t;
	}
    }

}
